using System.Security.Cryptography;
using System.Text;

namespace System.IO;
/// <summary>
/// 
/// </summary>
public static class StreamExtensions
{
    /// <summary>
    /// 
    /// </summary>
    public static int DefaultBufferSize { get; set; } = 81920;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static byte[] GetAllBytes(this Stream stream)
    {
        if (stream is MemoryStream memoryStream)
        {
            return memoryStream.ToArray();
        }

        using var ms = stream.CreateMemoryStream();

        return ms.ToArray();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static async Task<byte[]> GetAllBytesAsync(this Stream stream, CancellationToken cancellationToken = default)
    {
        if (stream is MemoryStream memoryStream)
        {
            return memoryStream.ToArray();
        }

        using var ms = await stream.CreateMemoryStreamAsync(cancellationToken);

        return ms.ToArray();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="destination"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public static Task CopyToAsync(this Stream stream, Stream destination, CancellationToken cancellationToken)
    {
        if (stream.CanSeek)
        {
            stream.Position = 0;
        }

        return stream.CopyToAsync(
            destination,
            DefaultBufferSize,
            cancellationToken
        );
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="stream"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public async static Task<MemoryStream> CreateMemoryStreamAsync(this Stream stream, CancellationToken cancellationToken = default)
    {
        if (stream.CanSeek)
        {
            stream.Position = 0;
        }

        var memoryStream = new MemoryStream();
        await stream.CopyToAsync(memoryStream, cancellationToken);

        if (stream.CanSeek)
        {
            stream.Position = 0;
        }

        memoryStream.Position = 0;
        return memoryStream;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="stream"></param>
    /// <returns></returns>
    public static MemoryStream CreateMemoryStream(this Stream stream)
    {
        if (stream.CanSeek)
        {
            stream.Position = 0;
        }

        var memoryStream = new MemoryStream();
        stream.CopyTo(memoryStream);

        if (stream.CanSeek)
        {
            stream.Position = 0;
        }

        memoryStream.Position = 0;
        return memoryStream;
    }
    /// <summary>
    /// 
    /// </summary>

    /// <returns></returns>
    public static string ToMd5(this Stream stream)
    {
        using var md5 = MD5.Create();

        byte[] buffers = md5.ComputeHash(stream);

        var sb = new StringBuilder();

        foreach (var @byte in buffers)
        {
            sb.Append($"{@byte:X2}");
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>

    /// <returns></returns>
    public static string ToSha1(this Stream stream)
    {
        using var sha = SHA1.Create();

        byte[] buffers = sha.ComputeHash(stream);

        var sb = new StringBuilder();

        foreach (var @byte in buffers)
        {
            sb.Append($"{@byte:X2}");
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static string ToSha256(this Stream stream)
    {
        using var sha = SHA256.Create();

        byte[] buffers = sha.ComputeHash(stream);

        var sb = new StringBuilder();

        foreach (var @byte in buffers)
        {
            sb.Append($"{@byte:X2}");
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static string ToSha384(this Stream stream)
    {
        using var sha = SHA384.Create();

        byte[] buffers = sha.ComputeHash(stream);

        var sb = new StringBuilder();

        foreach (var @byte in buffers)
        {
            sb.Append($"{@byte:X2}");
        }

        return sb.ToString();
    }
    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    public static string ToSha512(this Stream stream)
    {
        using var sha = SHA512.Create();

        byte[] buffers = sha.ComputeHash(stream);

        var sb = new StringBuilder();

        foreach (var @byte in buffers)
        {
            sb.Append($"{@byte:X2}");
        }

        return sb.ToString();
    }
}